﻿namespace Atomic.Data.EntityFramework
{
    using System.Threading;
    using System.Data.Entity;
    using System.Collections.Generic;

    using Atomic.Extensions;
    using Atomic.Environment;

    /// <summary>
    /// EF 数据上下文。
    /// </summary>
    public class AtomicObjectContext : DbContext, IDbContext
    {
        /// <summary>
        /// 启动初始化 DbContext 方式。
        /// </summary>
        private static AtomicInitializer _initializer;

        /// <summary>
        /// 锁定对象。
        /// </summary>
        private static object _initializerLock = new object();

        /// <summary>
        /// 原子化。
        /// </summary>
        private static bool _isInitialized;

        /// <summary>
        /// 初始化 EF 数据上下文。
        /// </summary>
        public AtomicObjectContext()
            : base(AtomicConfigurationManager.Current.ConnectionName)
        {
            // 确保每次启动应用程序时只初始化一次。
            LazyInitializer.EnsureInitialized(ref _initializer, ref _isInitialized, ref _initializerLock);
        }

        /// <summary>
        /// 实体模型绑定。
        /// </summary>
        /// <param name="modelBuilder">数据实体模型绑定器。</param>
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            AtomicContainer.Resolve<IEnumerable<IConfigurationMapper>>().ForEach(mapper => mapper.Execute(modelBuilder));
        }

        /// <summary>
        /// 创建一个原始 SQL 查询，该查询将返回给定类型的元素。类型可以是包含与从查询返回的列名匹配的属性的任何类型，也可以是简单的基元类型。
        /// </summary>
        /// <typeparam name="TElement">实体类型。</typeparam>
        /// <param name="sql">命令字符串。</param>
        /// <param name="parameters">要应用于命令字符串的参数。</param>
        /// <returns>实体集合。</returns>
        public IEnumerable<TElement> SqlQuery<TElement>(string sql, params object[] parameters)
        {
            return this.Database.SqlQuery<TElement>(sql, parameters);
        }

        /// <summary>
        /// 对数据库执行给定的 DDL/DML 命令。
        /// </summary>
        /// <param name="sql">命令字符串。</param>
        /// <param name="parameters">要应用于命令字符串的参数。</param>
        /// <returns>执行命令后由数据库返回的结果。</returns>
        public int ExecuteSqlCommand(string sql, params object[] parameters)
        {
            return this.Database.ExecuteSqlCommand(sql, parameters);
        }

        /// <summary>
        /// 启动初始化 DbContext 方式。
        /// </summary>
        private class AtomicInitializer
        {
            /// <summary>
            /// 初始化 DbContext 方式。
            /// </summary>
            public AtomicInitializer()
            {
                Database.SetInitializer<AtomicObjectContext>(null);
            }
        }
    }
}